const fs = require("fs").promises;
const path = require("path");
const { glob } = require("glob");

// Dynamic imports for ES modules
let chalk, ora, inquirer;

// Initialize ES modules
async function initializeModules() {
    chalk = (await import("chalk")).default;
    ora = (await import("ora")).default;
    inquirer = (await import("inquirer")).default;
}

class V1ToV2Upgrader {
    constructor() {
        // Constructor remains empty
    }

    async upgrade(options = {}) {
        try {
            // Initialize ES modules
            await initializeModules();
            // Keep readline open throughout the process
            process.stdin.resume();

            // 1. 欢迎消息
            console.log(
                chalk.bold("\n欢迎使用 SDAT-Method V1 到 V2 升级工具\n")
            );
            console.log(
                "此工具将帮助你将 SDAT-Method V1 项目升级到 V2 版本。\n"
            );
            console.log(chalk.cyan("此工具可以："));
            console.log("- 为你的 V1 文件创建备份 (.sdat-V1-backup/)");
            console.log("- 安装新的 V2 .sdat-core 结构");
            console.log("- 以新格式保留你的 PRD、架构和用户 Story \n");
            console.log(chalk.yellow("此工具不能："));
            console.log("- 修改你的文档内容（升级后使用 doc-migration-task）");
            console.log("- 触及 sdat-agent/ 和 docs/ 以外的任何文件\n");

            // 2. Get project path
            const projectPath = await this.getProjectPath(options.projectPath);

            // 3. Validate V1 structure
            const validation = await this.validateV1Project(projectPath);
            if (!validation.isValid) {
                console.error(chalk.red("\n错误: 这似乎不是一个 V1 项目。"));
                console.error("期望找到:");
                console.error("- sdat-agent/ 目录");
                console.error("- docs/ 目录\n");
                console.error("请检查你是否在正确的目录中，然后重试。");
                return;
            }

            // 4. Pre-flight check
            const analysis = await this.analyzeProject(projectPath);
            await this.showPreflightCheck(analysis, options);

            if (!options.dryRun) {
                const { confirm } = await inquirer.prompt([
                    {
                        type: "confirm",
                        name: "confirm",
                        message: "Continue with upgrade?",
                        default: true,
                    },
                ]);

                if (!confirm) {
                    console.log("Upgrade cancelled.");
                    return;
                }
            }

            // 5. Create backup
            if (options.backup !== false && !options.dryRun) {
                await this.createBackup(projectPath);
            }

            // 6. Install V2 structure
            if (!options.dryRun) {
                await this.installV2Structure(projectPath);
            }

            // 7. Migrate documents
            if (!options.dryRun) {
                await this.migrateDocuments(projectPath, analysis);
            }

            // 8. Setup IDE
            if (!options.dryRun) {
                await this.setupIDE(projectPath, options.ides);
            }

            // 9. Show completion report
            this.showCompletionReport(projectPath, analysis);

            process.exit(0);
        } catch (error) {
            console.error(chalk.red("\n升级错误:"), error.message);
            process.exit(1);
        }
    }

    async getProjectPath(providedPath) {
        if (providedPath) {
            return path.resolve(providedPath);
        }

        const { projectPath } = await inquirer.prompt([
            {
                type: "input",
                name: "projectPath",
                message: "Please enter the path to your V1 project:",
                default: process.cwd(),
            },
        ]);

        return path.resolve(projectPath);
    }

    async validateV1Project(projectPath) {
        const spinner = ora("Validating project structure...").start();

        try {
            const sdatAgentPath = path.join(projectPath, "sdat-agent");
            const docsPath = path.join(projectPath, "docs");

            const hasSdatAgent = await this.pathExists(sdatAgentPath);
            const hasDocs = await this.pathExists(docsPath);

            if (hasSdatAgent) {
                spinner.text = "✓ Found sdat-agent/ directory";
                console.log(chalk.green("\n✓ Found sdat-agent/ directory"));
            }

            if (hasDocs) {
                console.log(chalk.green("✓ Found docs/ directory"));
            }

            const isValid = hasSdatAgent && hasDocs;

            if (isValid) {
                spinner.succeed("This appears to be a valid V1 project");
            } else {
                spinner.fail("Invalid V1 project structure");
            }

            return { isValid, hasSdatAgent, hasDocs };
        } catch (error) {
            spinner.fail("Validation failed");
            throw error;
        }
    }

    async analyzeProject(projectPath) {
        const docsPath = path.join(projectPath, "docs");
        const sdatAgentPath = path.join(projectPath, "sdat-agent");

        // Find PRD
        const prdCandidates = ["prd.md", "PRD.md", "product-requirements.md"];
        let prdFile = null;
        for (const candidate of prdCandidates) {
            const candidatePath = path.join(docsPath, candidate);
            if (await this.pathExists(candidatePath)) {
                prdFile = candidate;
                break;
            }
        }

        // Find Architecture
        const archCandidates = [
            "architecture.md",
            "Architecture.md",
            "technical-architecture.md",
        ];
        let archFile = null;
        for (const candidate of archCandidates) {
            const candidatePath = path.join(docsPath, candidate);
            if (await this.pathExists(candidatePath)) {
                archFile = candidate;
                break;
            }
        }

        // Find Front-end Architecture (V1 specific)
        const frontEndCandidates = [
            "front-end-architecture.md",
            "frontend-architecture.md",
            "ui-architecture.md",
        ];
        let frontEndArchFile = null;
        for (const candidate of frontEndCandidates) {
            const candidatePath = path.join(docsPath, candidate);
            if (await this.pathExists(candidatePath)) {
                frontEndArchFile = candidate;
                break;
            }
        }

        // Find UX/UI spec
        const uxSpecCandidates = [
            "ux-ui-spec.md",
            "ux-ui-specification.md",
            "ui-spec.md",
            "ux-spec.md",
        ];
        let uxSpecFile = null;
        for (const candidate of uxSpecCandidates) {
            const candidatePath = path.join(docsPath, candidate);
            if (await this.pathExists(candidatePath)) {
                uxSpecFile = candidate;
                break;
            }
        }

        // Find v0 prompt or UX prompt
        const uxPromptCandidates = [
            "v0-prompt.md",
            "ux-prompt.md",
            "ui-prompt.md",
            "design-prompt.md",
        ];
        let uxPromptFile = null;
        for (const candidate of uxPromptCandidates) {
            const candidatePath = path.join(docsPath, candidate);
            if (await this.pathExists(candidatePath)) {
                uxPromptFile = candidate;
                break;
            }
        }

        // Find epic files
        const epicFiles = await glob("epic*.md", { cwd: docsPath });

        // Find story files
        const storiesPath = path.join(docsPath, "stories");
        let storyFiles = [];
        if (await this.pathExists(storiesPath)) {
            storyFiles = await glob("*.md", { cwd: storiesPath });
        }

        // Count custom files in sdat-agent
        const sdatAgentFiles = await glob("**/*.md", {
            cwd: sdatAgentPath,
            ignore: ["node_modules/**"],
        });

        return {
            prdFile,
            archFile,
            frontEndArchFile,
            uxSpecFile,
            uxPromptFile,
            epicFiles,
            storyFiles,
            customFileCount: sdatAgentFiles.length,
        };
    }

    async showPreflightCheck(analysis, options) {
        console.log(chalk.bold("\n项目分析:"));
        console.log(
            `- 找到 PRD: ${
                analysis.prdFile
                    ? `docs/${analysis.prdFile}`
                    : chalk.yellow("未找到")
            }`
        );
        console.log(
            `- 找到架构: ${
                analysis.archFile
                    ? `docs/${analysis.archFile}`
                    : chalk.yellow("未找到")
            }`
        );
        if (analysis.frontEndArchFile) {
            console.log(
                `- Front-end Architecture found: docs/${analysis.frontEndArchFile}`
            );
        }
        console.log(
            `- 找到 UX/UI 规范: ${
                analysis.uxSpecFile
                    ? `docs/${analysis.uxSpecFile}`
                    : chalk.yellow("未找到")
            }`
        );
        console.log(
            `- 找到 UX/设计提示: ${
                analysis.uxPromptFile
                    ? `docs/${analysis.uxPromptFile}`
                    : chalk.yellow("未找到")
            }`
        );
        console.log(
            `- 找到 Epic 文件: ${analysis.epicFiles.length} 个文件 (epic*.md)`
        );
        console.log(
            `- 找到用户 Story : docs/stories/ 目录中有 ${analysis.storyFiles.length} 个文件`
        );
        console.log(
            `- sdat-agent/ 中的自定义文件: ${analysis.customFileCount}`
        );

        if (!options.dryRun) {
            console.log("\n以下内容将备份到 .sdat-V1-backup/:");
            console.log("- sdat-agent/ (整个目录)");
            console.log("- docs/ (整个目录)");

            if (analysis.epicFiles.length > 0) {
                console.log(
                    chalk.green(
                        "\n注意: 找到 Epic 文件！它们将被放置在 docs/prd/ 目录中，并生成 index.md 文件。"
                    )
                );
                console.log(
                    chalk.green("由于 Epic 文件已存在，升级后你无需拆分 PRD。")
                );
            }
        }
    }

    async createBackup(projectPath) {
        const spinner = ora("正在创建备份...").start();

        try {
            const backupPath = path.join(projectPath, ".sdat-V1-backup");

            // 检查备份是否已存在
            if (await this.pathExists(backupPath)) {
                spinner.fail("备份目录已存在");
                console.error(
                    chalk.red("\n错误: 备份目录 .sdat-V1-backup/ 已存在。")
                );
                console.error("\n这可能意味着已经尝试过升级。");
                console.error("请删除或重命名现有备份，然后重试。");
                throw new Error("备份已存在");
            }

            // 创建备份目录
            await fs.mkdir(backupPath, { recursive: true });
            spinner.text = "✓ 创建了 .sdat-V1-backup/";
            console.log(chalk.green("\n✓ 创建了 .sdat-V1-backup/"));

            // 移动 sdat-agent
            const sdatAgentSrc = path.join(projectPath, "sdat-agent");
            const sdatAgentDest = path.join(backupPath, "sdat-agent");
            await fs.rename(sdatAgentSrc, sdatAgentDest);
            console.log(chalk.green("✓ 将 sdat-agent/ 移动到备份"));

            // 移动 docs
            const docsSrc = path.join(projectPath, "docs");
            const docsDest = path.join(backupPath, "docs");
            await fs.rename(docsSrc, docsDest);
            console.log(chalk.green("✓ 将 docs/ 移动到备份"));

            spinner.succeed("备份创建成功");
        } catch (error) {
            spinner.fail("备份失败");
            throw error;
        }
    }

    async installV2Structure(projectPath) {
        const spinner = ora("正在安装 V2 结构...").start();

        try {
            // 获取源 sdat-core 目录（不带点前缀）
            const sourcePath = path.join(__dirname, "..", "..", "sdat-core");
            const destPath = path.join(projectPath, ".sdat-core");

            // 复制 .sdat-core
            await this.copyDirectory(sourcePath, destPath);
            spinner.text = "✓ 从 V2 复制了新的 .sdat-core/ 目录";
            console.log(chalk.green("\n✓ 从 V2 复制了新的 .sdat-core/ 目录"));

            // 创建 docs 目录
            const docsPath = path.join(projectPath, "docs");
            await fs.mkdir(docsPath, { recursive: true });
            console.log(chalk.green("✓ 创建了新的 docs/ 目录"));

            // 为将来的更新创建安装清单
            await this.createInstallManifest(projectPath);
            console.log(chalk.green("✓ 创建了安装清单"));

            console.log(
                chalk.yellow("\n注意: 你的 V1 sdat-agent 内容已备份但未迁移。")
            );
            console.log(
                chalk.yellow("新的 V2 代理完全不同，并且需要不同的文件结构。")
            );

            spinner.succeed("V2 结构安装成功");
        } catch (error) {
            spinner.fail("V2 安装失败");
            throw error;
        }
    }

    async migrateDocuments(projectPath, analysis) {
        const spinner = ora("正在迁移你的项目文档...").start();

        try {
            const backupDocsPath = path.join(
                projectPath,
                ".sdat-V1-backup",
                "docs"
            );
            const newDocsPath = path.join(projectPath, "docs");
            let copiedCount = 0;

            // 复制 PRD
            if (analysis.prdFile) {
                const src = path.join(backupDocsPath, analysis.prdFile);
                const dest = path.join(newDocsPath, analysis.prdFile);
                await fs.copyFile(src, dest);
                console.log(
                    chalk.green(`\n✓ 已将 PRD 复制到 docs/${analysis.prdFile}`)
                );
                copiedCount++;
            }

            // 复制架构
            if (analysis.archFile) {
                const src = path.join(backupDocsPath, analysis.archFile);
                const dest = path.join(newDocsPath, analysis.archFile);
                await fs.copyFile(src, dest);
                console.log(
                    chalk.green(`✓ 已将架构复制到 docs/${analysis.archFile}`)
                );
                copiedCount++;
            }

            // 如果存在前端架构则复制
            if (analysis.frontEndArchFile) {
                const src = path.join(
                    backupDocsPath,
                    analysis.frontEndArchFile
                );
                const dest = path.join(newDocsPath, analysis.frontEndArchFile);
                await fs.copyFile(src, dest);
                console.log(
                    chalk.green(
                        `✓ 已将前端架构复制到 docs/${analysis.frontEndArchFile}`
                    )
                );
                console.log(
                    chalk.yellow(
                        "注意: V2 使用单一的 full-stack-architecture.md - 使用 doc-migration-task 进行合并"
                    )
                );
                copiedCount++;
            }

            // 如果存在 UX/UI 规范则复制
            if (analysis.uxSpecFile) {
                const src = path.join(backupDocsPath, analysis.uxSpecFile);
                const dest = path.join(newDocsPath, analysis.uxSpecFile);
                await fs.copyFile(src, dest);
                console.log(
                    chalk.green(
                        `✓ 已将 UX/UI 规范复制到 docs/${analysis.uxSpecFile}`
                    )
                );
                copiedCount++;
            }

            // 如果存在 UX/设计提示则复制
            if (analysis.uxPromptFile) {
                const src = path.join(backupDocsPath, analysis.uxPromptFile);
                const dest = path.join(newDocsPath, analysis.uxPromptFile);
                await fs.copyFile(src, dest);
                console.log(
                    chalk.green(
                        `✓ 已将 UX/设计提示复制到 docs/${analysis.uxPromptFile}`
                    )
                );
                copiedCount++;
            }

            // 复制用户 Story 
            if (analysis.storyFiles.length > 0) {
                const storiesDir = path.join(newDocsPath, "stories");
                await fs.mkdir(storiesDir, { recursive: true });

                for (const storyFile of analysis.storyFiles) {
                    const src = path.join(backupDocsPath, "stories", storyFile);
                    const dest = path.join(storiesDir, storyFile);
                    await fs.copyFile(src, dest);
                }
                console.log(
                    chalk.green(
                        `✓ 已将 ${analysis.storyFiles.length} 个用户 Story 文件复制到 docs/stories/`
                    )
                );
                copiedCount += analysis.storyFiles.length;
            }

            // 将 Epic 文件复制到 prd 子文件夹
            if (analysis.epicFiles.length > 0) {
                const prdDir = path.join(newDocsPath, "prd");
                await fs.mkdir(prdDir, { recursive: true });

                for (const epicFile of analysis.epicFiles) {
                    const src = path.join(backupDocsPath, epicFile);
                    const dest = path.join(prdDir, epicFile);
                    await fs.copyFile(src, dest);
                }
                console.log(
                    chalk.green(
                        `✓ 找到并复制了 ${analysis.epicFiles.length} 个 Epic 文件到 docs/prd/`
                    )
                );

                // Create index.md for the prd folder
                await this.createPrdIndex(projectPath, analysis);
                console.log(chalk.green("✓ 在 docs/prd/ 中创建了 index.md"));

                console.log(
                    chalk.green(
                        "\n注意: 检测到 Epic 文件！这些文件与 V2 兼容并已复制。"
                    )
                );
                console.log(
                    chalk.green("由于 Epic 文件已存在，你不需要拆分 PRD。")
                );
                copiedCount += analysis.epicFiles.length;
            }

            spinner.succeed(`成功迁移了 ${copiedCount} 个文档`);
        } catch (error) {
            spinner.fail("文档迁移失败");
            throw error;
        }
    }

    async setupIDE(projectPath, selectedIdes) {
        // 使用从安装程序传递的 IDE 选择
        if (!selectedIdes || selectedIdes.length === 0) {
            console.log(chalk.dim("未请求 IDE 设置 - 跳过"));
            return;
        }

        const ideSetup = require("../installer/lib/ide-setup");
        const spinner = ora("正在为所有代理设置 IDE 规则...").start();

        try {
            const ideMessages = {
                cursor: "Rules created in .cursor/rules/",
                "claude-code": "Commands created in .claude/commands/SDAT/",
                windsurf: "Rules created in .windsurf/rules/",
                trae: "Rules created in.trae/rules/",
                roo: "Custom modes created in .roomodes",
                cline: "Rules created in .clinerules/",
            };

            // Setup each selected IDE
            for (const ide of selectedIdes) {
                spinner.text = `Setting up ${ide}...`;
                await ideSetup.setup(ide, projectPath);
                console.log(chalk.green(`\n✓ ${ideMessages[ide]}`));
            }

            spinner.succeed(
                `IDE setup complete for ${selectedIdes.length} IDE(s)!`
            );
        } catch (error) {
            spinner.fail("IDE setup failed");
            console.error(
                chalk.yellow("IDE setup failed, but upgrade is complete.")
            );
        }
    }

    showCompletionReport(projectPath, analysis) {
        console.log(chalk.bold.green("\n✓ Upgrade Complete!\n"));
        console.log(chalk.bold("Summary:"));
        console.log(`- V1 files backed up to: .sdat-V1-backup/`);
        console.log(`- V2 structure installed: .sdat-core/ (fresh from V2)`);

        const totalDocs =
            (analysis.prdFile ? 1 : 0) +
            (analysis.archFile ? 1 : 0) +
            (analysis.frontEndArchFile ? 1 : 0) +
            (analysis.uxSpecFile ? 1 : 0) +
            (analysis.uxPromptFile ? 1 : 0) +
            analysis.storyFiles.length;
        console.log(
            `- Documents migrated: ${totalDocs} files${
                analysis.epicFiles.length > 0
                    ? ` + ${analysis.epicFiles.length} epics`
                    : ""
            }`
        );

        console.log(chalk.bold("\nImportant Changes:"));
        console.log(
            "- The V2 agents (sm, dev, etc.) expect different file structures than V1"
        );
        console.log(
            "- Your V1 sdat-agent content was NOT migrated (it's incompatible)"
        );
        if (analysis.epicFiles.length > 0) {
            console.log(
                "- Epic files were found and copied - no PRD sharding needed!"
            );
        }
        if (analysis.frontEndArchFile) {
            console.log(
                "- Front-end architecture found - V2 uses full-stack-architecture.md, migration needed"
            );
        }
        if (analysis.uxSpecFile || analysis.uxPromptFile) {
            console.log(
                "- UX/UI design files found and copied - ready for use with V2"
            );
        }

        console.log(chalk.bold("\nNext Steps:"));
        console.log("1. Review your documents in the new docs/ folder");
        console.log(
            "2. Use @sdat-master agent to run the doc-migration-task to align your documents with V2 templates"
        );
        if (analysis.epicFiles.length === 0) {
            console.log(
                "3. Use @sdat-master agent to shard the PRD to create epic files"
            );
        }

        console.log(
            chalk.dim(
                "\nYour V1 backup is preserved in .sdat-V1-backup/ and can be restored if needed."
            )
        );
    }

    async pathExists(filePath) {
        try {
            await fs.access(filePath);
            return true;
        } catch {
            return false;
        }
    }

    async copyDirectory(src, dest) {
        await fs.mkdir(dest, { recursive: true });
        const entries = await fs.readdir(src, { withFileTypes: true });

        for (const entry of entries) {
            const srcPath = path.join(src, entry.name);
            const destPath = path.join(dest, entry.name);

            if (entry.isDirectory()) {
                await this.copyDirectory(srcPath, destPath);
            } else {
                await fs.copyFile(srcPath, destPath);
            }
        }
    }

    async createPrdIndex(projectPath, analysis) {
        const prdIndexPath = path.join(projectPath, "docs", "prd", "index.md");
        const prdPath = path.join(
            projectPath,
            "docs",
            analysis.prdFile || "prd.md"
        );

        let indexContent = "# Product Requirements Document\n\n";

        // Try to read the PRD to get the title and intro content
        if (analysis.prdFile && (await this.pathExists(prdPath))) {
            try {
                const prdContent = await fs.readFile(prdPath, "utf8");
                const lines = prdContent.split("\n");

                // Find the first heading
                const titleMatch = lines.find((line) => line.startsWith("# "));
                if (titleMatch) {
                    indexContent = titleMatch + "\n\n";
                }

                // Get any content before the first ## section
                let introContent = "";
                let foundFirstSection = false;
                for (const line of lines) {
                    if (line.startsWith("## ")) {
                        foundFirstSection = true;
                        break;
                    }
                    if (!line.startsWith("# ")) {
                        introContent += line + "\n";
                    }
                }

                if (introContent.trim()) {
                    indexContent += introContent.trim() + "\n\n";
                }
            } catch (error) {
                // If we can't read the PRD, just use default content
            }
        }

        // Add sections list
        indexContent += "## Sections\n\n";

        // Sort epic files for consistent ordering
        const sortedEpics = [...analysis.epicFiles].sort();

        for (const epicFile of sortedEpics) {
            // Extract epic name from filename
            const epicName = epicFile
                .replace(/\.md$/, "")
                .replace(/^epic-?/i, "")
                .replace(/-/g, " ")
                .replace(/^\d+\s*/, "") // Remove leading numbers
                .trim();

            const displayName =
                epicName.charAt(0).toUpperCase() + epicName.slice(1);
            indexContent += `- [${
                displayName || epicFile.replace(".md", "")
            }](./${epicFile})\n`;
        }

        await fs.writeFile(prdIndexPath, indexContent);
    }

    async createInstallManifest(projectPath) {
        const fileManager = require("../installer/lib/file-manager");
        const { glob } = require("glob");

        // Get all files in .sdat-core for the manifest
        const sdatCorePath = path.join(projectPath, ".sdat-core");
        const files = await glob("**/*", {
            cwd: sdatCorePath,
            nodir: true,
            ignore: ["**/.git/**", "**/node_modules/**"],
        });

        // Prepend .sdat-core/ to file paths for manifest
        const manifestFiles = files.map((file) =>
            path.join(".sdat-core", file)
        );

        const config = {
            installType: "full",
            agent: null,
            ide: null, // Will be set if IDE setup is done later
        };

        await fileManager.createManifest(projectPath, config, manifestFiles);
    }
}

module.exports = V1ToV2Upgrader;
